package service

import (
	"math/rand/v2"
	"smart-flow/monitor/proto"
	"sync"

	cmap "github.com/orcaman/concurrent-map"
)

var (
	containersOnce sync.Once
	containersIns  *Containers
)

type Containers struct {
	containersV4 cmap.ConcurrentMap
	containersV6 cmap.ConcurrentMap
}

func ContainersInstance() *Containers {
	containersOnce.Do(func() {
		containersIns = &Containers{
			containersV4: cmap.New(),
			containersV6: cmap.New(),
		}
	})
	return containersIns
}

func (e *Containers) AddV4Container(ip string, info *proto.ContainerInfo) {
	e.containersV4.Set(ip, info)
}

func (e *Containers) GetV4Container(ip string) *proto.ContainerInfo {
	val, exist := e.containersV4.Get(ip)
	if !exist {
		return nil
	}
	return val.(*proto.ContainerInfo)
}

func (e *Containers) DelV4Container(ip string) {
	e.containersV4.Remove(ip)
}

func (e *Containers) ExistV4Container(ip string) bool {
	return e.containersV4.Has(ip)
}

func (e *Containers) GetAllV4Containers() []*proto.ContainerInfo {
	containers := make([]*proto.ContainerInfo, 0, e.containersV4.Count())
	e.containersV4.IterCb(func(key string, value any) {
		containers = append(containers, value.(*proto.ContainerInfo))
	})
	return containers
}

func (e *Containers) GetRandomV4Containers(max int) []*proto.ContainerInfo {
	count := e.containersV4.Count()
	containers := make([]*proto.ContainerInfo, 0, count)
	e.containersV4.IterCb(func(key string, value any) {
		containers = append(containers, value.(*proto.ContainerInfo))
	})
	diff := count - max
	if diff <= 0 {
		return containers
	}
	pos := rand.IntN(diff)
	return containers[pos : pos+max]
}

func (e *Containers) GetAllV4ContainerIps() []string {
	containerIps := make([]string, 0)
	e.containersV4.IterCb(func(key string, value any) {
		containerIps = append(containerIps, value.(*proto.ContainerInfo).ContainerIp)
	})
	return containerIps
}

func (e *Containers) GetV4ContainerCount() int {
	return e.containersV4.Count()
}

func (e *Containers) AddV6Container(ip string, info *proto.ContainerInfo) {
	e.containersV6.Set(ip, info)
}

func (e *Containers) GetV6Container(ip string) *proto.ContainerInfo {
	val, exist := e.containersV6.Get(ip)
	if !exist {
		return nil
	}
	return val.(*proto.ContainerInfo)
}

func (e *Containers) DelV6Container(ip string) {
	e.containersV6.Remove(ip)
}

func (e *Containers) ExistV6Container(ip string) bool {
	return e.containersV6.Has(ip)
}

func (e *Containers) GetAllV6Containers() []*proto.ContainerInfo {
	containers := make([]*proto.ContainerInfo, 0)
	e.containersV6.IterCb(func(key string, value any) {
		containers = append(containers, value.(*proto.ContainerInfo))
	})
	return containers
}

func (e *Containers) GetRandomV6Containers(max int) []*proto.ContainerInfo {
	count := e.containersV6.Count()
	containers := make([]*proto.ContainerInfo, 0, count)
	e.containersV6.IterCb(func(key string, value any) {
		containers = append(containers, value.(*proto.ContainerInfo))
	})
	diff := count - max
	if diff <= 0 {
		return containers
	}
	pos := rand.IntN(diff)
	return containers[pos : pos+max]
}

func (e *Containers) GetAllV6ContainerIps() []string {
	containerIps := make([]string, 0)
	e.containersV6.IterCb(func(key string, value any) {
		containerIps = append(containerIps, value.(*proto.ContainerInfo).ContainerIp)
	})
	return containerIps
}

func (e *Containers) GetV6ContainerCount() int {
	return e.containersV6.Count()
}
